#include "p2p/transport.h"

#include "base/common.h"
#include "base/logging.h"
#include "p2p/candidate.h"
#include "p2p/constants.h"
#include "p2p/sessionmanager.h"
#include "p2p/parsing.h"
#include "p2p/transportchannelimpl.h"
#include "xmllite/xmlelement.h"
#include "xmpp/constants.h"

namespace cricket {

	enum {
		MSG_CREATECHANNEL = 1,
		MSG_DESTROYCHANNEL = 2,
		MSG_DESTROYALLCHANNELS = 3,
		MSG_CONNECTCHANNELS = 4,
		MSG_RESETCHANNELS = 5,
		MSG_ONSIGNALINGREADY = 6,
		MSG_ONREMOTECANDIDATE = 7,
		MSG_READSTATE = 8,
		MSG_WRITESTATE = 9,
		MSG_REQUESTSIGNALING = 10,
		MSG_CANDIDATEREADY = 11,
		MSG_ROUTECHANGE = 12,
		MSG_CONNECTING = 13,
		MSG_CANDIDATEALLOCATIONCOMPLETE = 14,
		MSG_ROLECONFLICT = 15,
		MSG_SETROLE = 16,
		MSG_SETLOCALDESCRIPTION = 17,
		MSG_SETREMOTEDESCRIPTION = 18,
		MSG_GETSTATS = 19
	};

	struct ChannelParams : public base::MessageData {
		ChannelParams() : channel(NULL), candidate(NULL) {}
		explicit ChannelParams(int component)
			: component(component), channel(NULL), candidate(NULL) {}
		explicit ChannelParams(Candidate* candidate)
			: channel(NULL), candidate(candidate) {
		}

		~ChannelParams() {
			delete candidate;
		}

		std::string name;
		int component;
		TransportChannelImpl* channel;
		Candidate* candidate;
	};

	struct TransportDescriptionParams : public base::MessageData {
		TransportDescriptionParams(const TransportDescription& desc,
			ContentAction action)
			: desc(desc), action(action), result(false) {}
		const TransportDescription& desc;
		ContentAction action;
		bool result;
	};

	struct TransportRoleParam : public base::MessageData {
		explicit TransportRoleParam(TransportRole role) : role(role) {}

		TransportRole role;
	};

	struct StatsParam : public base::MessageData {
		explicit StatsParam(TransportStats* stats)
			: stats(stats), result(false) {}

		TransportStats* stats;
		bool result;
	};

	Transport::Transport(base::Thread* signaling_thread,
		base::Thread* worker_thread,
		const std::string& content_name,
		const std::string& type,
		PortAllocator* allocator)
		: signaling_thread_(signaling_thread),
		worker_thread_(worker_thread),
		content_name_(content_name),
		type_(type),
		allocator_(allocator),
		destroyed_(false),
		readable_(TRANSPORT_STATE_NONE),
		writable_(TRANSPORT_STATE_NONE),
		was_writable_(false),
		connect_requested_(false),
		role_(ROLE_UNKNOWN),
		tiebreaker_(0),
		protocol_(ICEPROTO_HYBRID),
		remote_ice_mode_(ICEMODE_FULL) {
	}

	Transport::~Transport() {
		ASSERT(signaling_thread_->IsCurrent());
		ASSERT(destroyed_);
	}

	void Transport::SetRole(TransportRole role) {
		TransportRoleParam param(role);
		worker_thread()->Send(this, MSG_SETROLE, &param);
	}

	bool Transport::SetLocalTransportDescription(
		const TransportDescription& description, ContentAction action) {
			TransportDescriptionParams params(description, action);
			worker_thread()->Send(this, MSG_SETLOCALDESCRIPTION, &params);
			return params.result;
	}

	bool Transport::SetRemoteTransportDescription(
		const TransportDescription& description, ContentAction action) {
			TransportDescriptionParams params(description, action);
			worker_thread()->Send(this, MSG_SETREMOTEDESCRIPTION, &params);
			return params.result;
	}

	TransportChannelImpl* Transport::CreateChannel(int component) {
		ChannelParams params(component);
		worker_thread()->Send(this, MSG_CREATECHANNEL, &params);
		return params.channel;
	}

	TransportChannelImpl* Transport::CreateChannel_w(int component) {
		ASSERT(worker_thread()->IsCurrent());
		TransportChannelImpl *impl;
		base::CritScope cs(&crit_);

		// Create the entry if it does not exist.
		bool impl_exists = false;
		if (channels_.find(component) == channels_.end()) {
			impl = CreateTransportChannel(component);
			channels_[component] = ChannelMapEntry(impl);
		} else {
			impl = channels_[component].get();
			impl_exists = true;
		}

		// Increase the ref count.
		channels_[component].AddRef();
		destroyed_ = false;

		if (impl_exists) {
			// If this is an existing channel, we should just return it without
			// connecting to all the signal again.
			return impl;
		}

		// Push down our transport state to the new channel.
		impl->SetRole(role_);
		impl->SetTiebreaker(tiebreaker_);
		if (local_description_) {
			ApplyLocalTransportDescription_w(impl);
			if (remote_description_) {
				ApplyRemoteTransportDescription_w(impl);
				ApplyNegotiatedTransportDescription_w(impl);
			}
		}

		impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState);
		impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
		impl->SignalRequestSignaling.connect(
			this, &Transport::OnChannelRequestSignaling);
		impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady);
		impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange);
		impl->SignalCandidatesAllocationDone.connect(
			this, &Transport::OnChannelCandidatesAllocationDone);
		impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict);

		if (connect_requested_) {
			impl->Connect();
			if (channels_.size() == 1) {
				// If this is the first channel, then indicate that we have started
				// connecting.
				signaling_thread()->Post(this, MSG_CONNECTING, NULL);
			}
		}
		return impl;
	}

	TransportChannelImpl* Transport::GetChannel(int component) {
		base::CritScope cs(&crit_);
		ChannelMap::iterator iter = channels_.find(component);
		return (iter != channels_.end()) ? iter->second.get() : NULL;
	}

	bool Transport::HasChannels() {
		base::CritScope cs(&crit_);
		return !channels_.empty();
	}

	void Transport::DestroyChannel(int component) {
		ChannelParams params(component);
		worker_thread()->Send(this, MSG_DESTROYCHANNEL, &params);
	}

	void Transport::DestroyChannel_w(int component) {
		ASSERT(worker_thread()->IsCurrent());

		TransportChannelImpl* impl = NULL;
		{
			base::CritScope cs(&crit_);
			ChannelMap::iterator iter = channels_.find(component);
			if (iter == channels_.end())
				return;

			iter->second.DecRef();
			if (!iter->second.ref()) {
				impl = iter->second.get();
				channels_.erase(iter);
			}
		}

		if (connect_requested_ && channels_.empty()) {
			// We're no longer attempting to connect.
			signaling_thread()->Post(this, MSG_CONNECTING, NULL);
		}

		if (impl) {
			// Check in case the deleted channel was the only non-writable channel.
			OnChannelWritableState(impl);
			DestroyTransportChannel(impl);
		}
	}

	void Transport::ConnectChannels() {
		ASSERT(signaling_thread()->IsCurrent());
		worker_thread()->Send(this, MSG_CONNECTCHANNELS, NULL);
	}

	void Transport::ConnectChannels_w() {
		ASSERT(worker_thread()->IsCurrent());
		if (connect_requested_ || channels_.empty())
			return;
		connect_requested_ = true;
		signaling_thread()->Post(
			this, MSG_CANDIDATEREADY, NULL);

		if (!local_description_) {
			// TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here.
			// As Transport must know TD is offer or answer and cricket::Transport
			// doesn't have the capability to decide it. This should be set by the
			// Session.
			// Session must generate local TD before remote candidates pushed when
			// initiate request initiated by the remote.
			LOG(LS_INFO) << "Transport::ConnectChannels_w: No local description has "
				<< "been set. Will generate one.";
			TransportDescription desc(NS_GINGLE_P2P, std::vector<std::string>(),
				base::CreateRandomString(ICE_UFRAG_LENGTH),
				base::CreateRandomString(ICE_PWD_LENGTH),
				ICEMODE_FULL, NULL, Candidates());
			SetLocalTransportDescription_w(desc, CA_OFFER);
		}

		CallChannels_w(&TransportChannelImpl::Connect);
		if (!channels_.empty()) {
			signaling_thread()->Post(this, MSG_CONNECTING, NULL);
		}
	}

	void Transport::OnConnecting_s() {
		ASSERT(signaling_thread()->IsCurrent());
		SignalConnecting(this);
	}

	void Transport::DestroyAllChannels() {
		ASSERT(signaling_thread()->IsCurrent());
		worker_thread()->Send(this, MSG_DESTROYALLCHANNELS, NULL);
		worker_thread()->Clear(this);
		signaling_thread()->Clear(this);
		destroyed_ = true;
	}

	void Transport::DestroyAllChannels_w() {
		ASSERT(worker_thread()->IsCurrent());
		std::vector<TransportChannelImpl*> impls;
		{
			base::CritScope cs(&crit_);
			for (ChannelMap::iterator iter = channels_.begin();
				iter != channels_.end();
				++iter) {
					iter->second.DecRef();
					if (!iter->second.ref())
						impls.push_back(iter->second.get());
			}
		}
		channels_.clear();


		for (size_t i = 0; i < impls.size(); ++i)
			DestroyTransportChannel(impls[i]);
	}

	void Transport::ResetChannels() {
		ASSERT(signaling_thread()->IsCurrent());
		worker_thread()->Send(this, MSG_RESETCHANNELS, NULL);
	}

	void Transport::ResetChannels_w() {
		ASSERT(worker_thread()->IsCurrent());

		// We are no longer attempting to connect
		connect_requested_ = false;

		// Clear out the old messages, they aren't relevant
		base::CritScope cs(&crit_);
		ready_candidates_.clear();

		// Reset all of the channels
		CallChannels_w(&TransportChannelImpl::Reset);
	}

	void Transport::OnSignalingReady() {
		ASSERT(signaling_thread()->IsCurrent());
		if (destroyed_) return;

		worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL);

		// Notify the subclass.
		OnTransportSignalingReady();
	}

	void Transport::CallChannels_w(TransportChannelFunc func) {
		ASSERT(worker_thread()->IsCurrent());
		base::CritScope cs(&crit_);
		for (ChannelMap::iterator iter = channels_.begin();
			iter != channels_.end();
			++iter) {
				((iter->second.get())->*func)();
		}
	}

	bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) {
		// No address zero.
		if (cand.address().IsNil() || cand.address().IsAny()) {
			*error = "candidate has address of zero";
			return false;
		}

		// Disallow all ports below 1024, except for 80 and 443 on public addresses.
		int port = cand.address().port();
		if (port < 1024) {
			if ((port != 80) && (port != 443)) {
				*error = "candidate has port below 1024, but not 80 or 443";
				return false;
			}

			if (cand.address().IsPrivateIP()) {
				*error = "candidate has port of 80 or 443 with private IP address";
				return false;
			}
		}

		return true;
	}


	bool Transport::GetStats(TransportStats* stats) {
		ASSERT(signaling_thread()->IsCurrent());
		StatsParam params(stats);
		worker_thread()->Send(this, MSG_GETSTATS, &params);
		return params.result;
	}

	bool Transport::GetStats_w(TransportStats* stats) {
		ASSERT(worker_thread()->IsCurrent());
		stats->content_name = content_name();
		stats->channel_stats.clear();
		for (ChannelMap::iterator iter = channels_.begin();
			iter != channels_.end();
			++iter) {
				TransportChannelStats substats;
				substats.component = iter->second->component();
				if (!iter->second->GetStats(&substats.connection_infos)) {
					return false;
				}
				stats->channel_stats.push_back(substats);
		}
		return true;
	}

	void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) {
		for (std::vector<Candidate>::const_iterator iter = candidates.begin();
			iter != candidates.end();
			++iter) {
				OnRemoteCandidate(*iter);
		}
	}

	void Transport::OnRemoteCandidate(const Candidate& candidate) {
		ASSERT(signaling_thread()->IsCurrent());
		if (destroyed_) return;

		if (!HasChannel(candidate.component())) {
			LOG(LS_WARNING) << "Ignoring candidate for unknown component "
				<< candidate.component();
			return;
		}

		ChannelParams* params = new ChannelParams(new Candidate(candidate));
		worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, params);
	}

	void Transport::OnRemoteCandidate_w(const Candidate& candidate) {
		ASSERT(worker_thread()->IsCurrent());
		ChannelMap::iterator iter = channels_.find(candidate.component());
		// It's ok for a channel to go away while this message is in transit.
		if (iter != channels_.end()) {
			iter->second->OnCandidate(candidate);
		}
	}

	void Transport::OnChannelReadableState(TransportChannel* channel) {
		ASSERT(worker_thread()->IsCurrent());
		signaling_thread()->Post(this, MSG_READSTATE, NULL);
	}

	void Transport::OnChannelReadableState_s() {
		ASSERT(signaling_thread()->IsCurrent());
		TransportState readable = GetTransportState_s(true);
		if (readable_ != readable) {
			readable_ = readable;
			SignalReadableState(this);
		}
	}

	void Transport::OnChannelWritableState(TransportChannel* channel) {
		ASSERT(worker_thread()->IsCurrent());
		signaling_thread()->Post(this, MSG_WRITESTATE, NULL);
	}

	void Transport::OnChannelWritableState_s() {
		ASSERT(signaling_thread()->IsCurrent());
		TransportState writable = GetTransportState_s(false);
		if (writable_ != writable) {
			was_writable_ = (writable_ == TRANSPORT_STATE_ALL);
			writable_ = writable;
			SignalWritableState(this);
		}
	}

	TransportState Transport::GetTransportState_s(bool read) {
		ASSERT(signaling_thread()->IsCurrent());
		base::CritScope cs(&crit_);
		bool any = false;
		bool all = !channels_.empty();
		for (ChannelMap::iterator iter = channels_.begin();
			iter != channels_.end();
			++iter) {
				bool b = (read ? iter->second->readable() :
					iter->second->writable());
				any = any || b;
				all = all && b;
		}
		if (all) {
			return TRANSPORT_STATE_ALL;
		} else if (any) {
			return TRANSPORT_STATE_SOME;
		} else {
			return TRANSPORT_STATE_NONE;
		}
	}

	void Transport::OnChannelRequestSignaling(TransportChannelImpl* channel) {
		ASSERT(worker_thread()->IsCurrent());
		ChannelParams* params = new ChannelParams(channel->component());
		signaling_thread()->Post(this, MSG_REQUESTSIGNALING, params);
	}

	void Transport::OnChannelRequestSignaling_s(int component) {
		ASSERT(signaling_thread()->IsCurrent());
		LOG(LS_INFO) << "Transport: " << content_name_ << ", allocating candidates";
		// Resetting ICE state for the channel.
		{
			base::CritScope cs(&crit_);
			ChannelMap::iterator iter = channels_.find(component);
			if (iter != channels_.end())
				iter->second.set_candidates_allocated(false);
		}
		SignalRequestSignaling(this);
	}

	void Transport::OnChannelCandidateReady(TransportChannelImpl* channel,
		const Candidate& candidate) {
			ASSERT(worker_thread()->IsCurrent());
			base::CritScope cs(&crit_);
			ready_candidates_.push_back(candidate);

			// We hold any messages until the client lets us connect.
			if (connect_requested_) {
				signaling_thread()->Post(
					this, MSG_CANDIDATEREADY, NULL);
			}
	}

	void Transport::OnChannelCandidateReady_s() {
		ASSERT(signaling_thread()->IsCurrent());
		ASSERT(connect_requested_);

		std::vector<Candidate> candidates;
		{
			base::CritScope cs(&crit_);
			candidates.swap(ready_candidates_);
		}

		// we do the deleting of Candidate* here to keep the new above and
		// delete below close to each other
		if (!candidates.empty()) {
			SignalCandidatesReady(this, candidates);
		}
	}

	void Transport::OnChannelRouteChange(TransportChannel* channel,
		const Candidate& remote_candidate) {
			ASSERT(worker_thread()->IsCurrent());
			ChannelParams* params = new ChannelParams(new Candidate(remote_candidate));
			params->channel = static_cast<cricket::TransportChannelImpl*>(channel);
			signaling_thread()->Post(this, MSG_ROUTECHANGE, params);
	}

	void Transport::OnChannelRouteChange_s(const TransportChannel* channel,
		const Candidate& remote_candidate) {
			ASSERT(signaling_thread()->IsCurrent());
			SignalRouteChange(this, remote_candidate.component(), remote_candidate);
	}

	void Transport::OnChannelCandidatesAllocationDone(
		TransportChannelImpl* channel) {
			ASSERT(worker_thread()->IsCurrent());
			base::CritScope cs(&crit_);
			ChannelMap::iterator iter = channels_.find(channel->component());
			ASSERT(iter != channels_.end());
			LOG(LS_INFO) << "Transport: " << content_name_ << ", component " 
				<< channel->component() << " allocation complete";
			iter->second.set_candidates_allocated(true);

			// If all channels belonging to this Transport got signal, then
			// forward this signal to upper layer.
			// Can this signal arrive before all transport channels are created?
			for (iter = channels_.begin(); iter != channels_.end(); ++iter) {
				if (!iter->second.candidates_allocated())
					return;
			}
			signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE);
	}

	void Transport::OnChannelCandidatesAllocationDone_s() {
		ASSERT(signaling_thread()->IsCurrent());
		LOG(LS_INFO) << "Transport: " << content_name_ << " allocation complete";
		SignalCandidatesAllocationDone(this);
	}

	void Transport::OnRoleConflict(TransportChannelImpl* channel) {
		signaling_thread_->Post(this, MSG_ROLECONFLICT);
	}

	void Transport::SetRole_w(TransportRole role) {
		base::CritScope cs(&crit_);
		role_ = role;
		for (ChannelMap::iterator iter = channels_.begin();
			iter != channels_.end(); ++iter) {
				iter->second->SetRole(role_);
		}
	}

	void Transport::SetRemoteIceMode_w(IceMode mode) {
		base::CritScope cs(&crit_);
		remote_ice_mode_ = mode;
		// Shouldn't channels be created after this method executed?
		for (ChannelMap::iterator iter = channels_.begin();
			iter != channels_.end(); ++iter) {
				iter->second->SetRemoteIceMode(remote_ice_mode_);
		}
	}

	bool Transport::SetLocalTransportDescription_w(
		const TransportDescription& desc, ContentAction action) {
			bool ret = true;
			base::CritScope cs(&crit_);
			local_description_.reset(new TransportDescription(desc));

			for (ChannelMap::iterator iter = channels_.begin();
				iter != channels_.end(); ++iter) {
					ret &= ApplyLocalTransportDescription_w(iter->second.get());
			}
			if (!ret)
				return false;

			// If PRANSWER/ANSWER is set, we should decide transport protocol type.
			if (action == CA_PRANSWER || action == CA_ANSWER) {
				ret &= NegotiateTransportDescription_w(action);
			}
			return ret;
	}

	bool Transport::SetRemoteTransportDescription_w(
		const TransportDescription& desc, ContentAction action) {
			bool ret = true;
			base::CritScope cs(&crit_);
			remote_description_.reset(new TransportDescription(desc));

			for (ChannelMap::iterator iter = channels_.begin();
				iter != channels_.end(); ++iter) {
					ret &= ApplyRemoteTransportDescription_w(iter->second.get());
			}

			// If PRANSWER/ANSWER is set, we should decide transport protocol type.
			if (action == CA_PRANSWER || action == CA_ANSWER) {
				ret = NegotiateTransportDescription_w(CA_OFFER);
			}
			return ret;
	}

	bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch) {
		ch->SetIceCredentials(local_description_->ice_ufrag,
			local_description_->ice_pwd);
		return true;
	}

	bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch) {
		ch->SetRemoteIceCredentials(remote_description_->ice_ufrag,
			remote_description_->ice_ufrag);
		return true;
	}

	void Transport::ApplyNegotiatedTransportDescription_w(
		TransportChannelImpl* channel) {
			channel->SetIceProtocolType(protocol_);
			channel->SetRemoteIceMode(remote_ice_mode_);
	}

	bool Transport::NegotiateTransportDescription_w(ContentAction local_role_) {
		// TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into
		// P2PTransport.
		const TransportDescription* offer;
		const TransportDescription* answer;

		if (local_role_ == CA_OFFER) {
			offer = local_description_.get();
			answer = remote_description_.get();
		} else {
			offer = remote_description_.get();
			answer = local_description_.get();
		}

		TransportProtocol offer_proto = TransportProtocolFromDescription(offer);
		TransportProtocol answer_proto = TransportProtocolFromDescription(answer);

		// If offered protocol is gice/ice, then we expect to receive matching
		// protocol in answer, anything else is treated as an error.
		// HYBRID is not an option when offered specific protocol.
		// If offered protocol is HYBRID and answered protocol is HYBRID then
		// gice is preferred protocol.
		// TODO(mallinath) - Answer from local or remote should't have both ice
		// and gice support. It should always pick which protocol it wants to use.
		// Once WebRTC stops supporting gice (for backward compatibility), HYBRID in
		// answer must be treated as error.
		if ((offer_proto == ICEPROTO_GOOGLE || offer_proto == ICEPROTO_RFC5245) &&
			(offer_proto != answer_proto)) {
				return false;
		}
		protocol_ = answer_proto == ICEPROTO_HYBRID ? ICEPROTO_GOOGLE : answer_proto;

		// If transport is in ROLE_CONTROLLED and remote end point supports only
		// ice_lite, this local end point should take CONTROLLING role.
		if (role_ == ROLE_CONTROLLED &&
			remote_description_->ice_mode == ICEMODE_LITE) {
				SetRole_w(ROLE_CONTROLLING);
		}

		// Update remote ice_mode to all existing channels.
		remote_ice_mode_ = remote_description_->ice_mode;

		// Now that we have negotiated everything, push it downward.
		// Note that we cache the result so that if we have race conditions
		// between future SetRemote/SetLocal invocations and new channel
		// creation, we have the negotiation state saved until a new
		// negotiation happens.
		for (ChannelMap::iterator iter = channels_.begin();
			iter != channels_.end();
			++iter) {
				ApplyNegotiatedTransportDescription_w(iter->second.get());
		}
		return true;
	}

	void Transport::OnMessage(base::Message* msg) {
		switch (msg->message_id) {
		case MSG_CREATECHANNEL: {
			ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
			params->channel = CreateChannel_w(params->component);
								}
								break;
		case MSG_DESTROYCHANNEL: {
			ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
			DestroyChannel_w(params->component);
								 }
								 break;
		case MSG_CONNECTCHANNELS:
			ConnectChannels_w();
			break;
		case MSG_RESETCHANNELS:
			ResetChannels_w();
			break;
		case MSG_DESTROYALLCHANNELS:
			DestroyAllChannels_w();
			break;
		case MSG_ONSIGNALINGREADY:
			CallChannels_w(&TransportChannelImpl::OnSignalingReady);
			break;
		case MSG_ONREMOTECANDIDATE: {
			ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
			OnRemoteCandidate_w(*params->candidate);
			delete params;
									}
									break;
		case MSG_CONNECTING:
			OnConnecting_s();
			break;
		case MSG_READSTATE:
			OnChannelReadableState_s();
			break;
		case MSG_WRITESTATE:
			OnChannelWritableState_s();
			break;
		case MSG_REQUESTSIGNALING: {
			ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
			OnChannelRequestSignaling_s(params->component);
			delete params;
								   }
								   break;
		case MSG_CANDIDATEREADY:
			OnChannelCandidateReady_s();
			break;
		case MSG_ROUTECHANGE: {
			ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
			OnChannelRouteChange_s(params->channel, *params->candidate);
			delete params;
							  }
							  break;
		case MSG_CANDIDATEALLOCATIONCOMPLETE:
			OnChannelCandidatesAllocationDone_s();
			break;
		case MSG_ROLECONFLICT:
			SignalRoleConflict();
			break;
		case MSG_SETROLE: {
			TransportRoleParam* param =
				static_cast<TransportRoleParam*>(msg->pdata);
			SetRole_w(param->role);
						  }
						  break;
		case MSG_SETLOCALDESCRIPTION: {
			TransportDescriptionParams* params =
				static_cast<TransportDescriptionParams*>(msg->pdata);
			params->result = SetLocalTransportDescription_w(params->desc,
				params->action);
									  }
									  break;
		case MSG_SETREMOTEDESCRIPTION: {
			TransportDescriptionParams* params =
				static_cast<TransportDescriptionParams*>(msg->pdata);
			params->result = SetRemoteTransportDescription_w(params->desc,
				params->action);
									   }
									   break;
		case MSG_GETSTATS: {
			StatsParam* params = static_cast<StatsParam*>(msg->pdata);
			params->result = GetStats_w(params->stats);
						   }
						   break;
		}
	}

	bool TransportParser::ParseAddress(const buzz::XmlElement* elem,
		const buzz::QName& address_name,
		const buzz::QName& port_name,
		base::SocketAddress* address,
		ParseError* error) {
			if (!elem->HasAttr(address_name))
				return BadParse("address does not have " + address_name.LocalPart(), error);
			if (!elem->HasAttr(port_name))
				return BadParse("address does not have " + port_name.LocalPart(), error);

			address->SetIP(elem->Attr(address_name));
			std::istringstream ist(elem->Attr(port_name));
			int port = 0;
			ist >> port;
			address->SetPort(port);

			return true;
	}

	// We're GICE if the namespace is NS_GOOGLE_P2P, or if NS_JINGLE_ICE_UDP is
	// used and the GICE ice-option is set.
	TransportProtocol TransportProtocolFromDescription(
		const TransportDescription* desc) {
			ASSERT(desc != NULL);
			if (desc->transport_type == NS_JINGLE_ICE_UDP) {
				return (desc->HasOption(ICE_OPTION_GICE)) ?
ICEPROTO_HYBRID : ICEPROTO_RFC5245;
			}
			return ICEPROTO_GOOGLE;
	}

}  // namespace cricket
